#include <glib.h>

static gboolean cbk_opt_flag = FALSE;
static gint cbk_opt_int = 0;

static gboolean callback_parse_optional (const gchar *option_name, const gchar *value,
		 gpointer data, GError **error)
{
	cbk_opt_flag = TRUE;
	if (value)
		cbk_opt_int = atoi (value);
	else
		cbk_opt_int = 0;
	return TRUE;
}

static int g_option_test(gint argc, gchar **argv)
{
    gboolean ret = -1;
    guint index = 0;
    GError *error = NULL;
    GOptionContext *context = NULL;

    gchar *main_str = NULL;
    gint   main_int = 0;
    gchar *main_file = NULL;
    gdouble main_double = 0.0;
    gint64 main_int64 = 0;
    gboolean main_bool = FALSE;
    gchar **main_array = NULL;
    gchar **main_remain = NULL;

    GOptionEntry main_entries[] = {
        { "string", 's', 0, G_OPTION_ARG_STRING, &main_str, "string type", "str" },
        { "int", 'i', 0, G_OPTION_ARG_INT, &main_int, "int type", "int_value" },
        { "file", 'f', 0, G_OPTION_ARG_FILENAME, &main_file, NULL, NULL },
        { "double", 'd', 0, G_OPTION_ARG_DOUBLE, &main_double, NULL, NULL },
        { "int64", 'l', 0, G_OPTION_ARG_INT64, &main_int64, NULL, NULL },
        { "bool", 'b', 0, G_OPTION_ARG_NONE, &main_bool, "bool type, -b with no value, no arg", NULL },
        { "array", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &main_array, "array type -a 1, -a 2", NULL },
        { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &main_remain, "Rest goes here", "REST" },
        { NULL }
    };

    guint main2_uint = 0;
    gint main2_hide_int = 0;
    gchar *main2_hide_str = NULL;

    GOptionEntry main_entries2[] = {
        { "uint", 'U', 0, G_OPTION_ARG_INT, &main2_uint, "unsigned int type", "U" },
        { "hide-int", 'I', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &main2_hide_int, "option will be hidden, but it still works", NULL },
        { "hide-str", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &main2_hide_str, "option will be hidden, but it still works", NULL },
        { NULL }
    };

    GOptionEntry main_entries3[] = {
        { "optional", 'O', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, callback_parse_optional, "optional", "int value" },
        { NULL }
    };

    GOptionGroup *group1;
    GOptionGroup *group2;

    gint group1_int = 0;
    gchar *group1_str = NULL;
    GOptionEntry group1_entries[] = {
        { "grp1-int", 'X', 0, G_OPTION_ARG_INT, &group1_int, "group1 int", "X" },
        { "grp1-str", 0, 0, G_OPTION_ARG_STRING, &group1_str, "string type", "str" },
        { NULL }
    };
    gint group2_int = 0;
    gchar *group2_str = NULL;
    GOptionEntry group2_entries[] = {
        { "grp2-int", 'Y', 0, G_OPTION_ARG_INT, &group2_int, "group2 int", "Y" },
        { "grp2-str", 0, 0, G_OPTION_ARG_STRING, &group2_str, "string type", "str" },
        { NULL }
    };

    if(argc < 2) {
        g_print("Help: %s -h | -? | --help | --help-all \n", argv[0]);
        return -1;
    }

    context = g_option_context_new("< g_option_context_new argument parameter string >");

    g_option_context_set_summary (context, "context summary");
    g_option_context_set_description(context, "context description");
    //g_option_context_set_ignore_unknown_options(context, TRUE);

    g_option_context_add_main_entries(context, main_entries, NULL);
    g_option_context_add_main_entries(context, main_entries2, NULL);
    g_option_context_add_main_entries(context, main_entries3, NULL);

    group1 = g_option_group_new ("group1", "Group1-Description", "Group1-Help", NULL, NULL);
    g_option_group_add_entries (group1, group1_entries);
    g_option_context_add_group (context, group1);

    group2 = g_option_group_new ("group2", "Group2-Description", "Group2-Help", NULL, NULL);
    g_option_group_add_entries (group2, group2_entries);
    g_option_context_add_group (context, group2);

    ret = g_option_context_parse(context, &argc, &argv, &error);
    if(TRUE != ret) {
        g_print("option parse failed: %s \n", error->message);
        return -1;
    }

#if 1 // main_entries
    g_print("main_str: %s \n", main_str);
    g_print("main_int: %d \n", main_int);
    g_print("main_file: %s \n", main_file);
    g_print("main_double: %f \n", main_double);
    g_print("main_int64: %4lld \n", main_int64);
    g_print("main_bool: %s \n", main_bool?"TRUE":"FALSE");

    index = 0;
    while((NULL != main_array) && (NULL != main_array[index]) ) {
        g_print("main_array[%d]: %s \n", index, main_array[index]);
        index ++;
    }
    index = 0;
    while((NULL != main_remain) && (NULL != main_remain[index]) ) {
        g_print("main_remain[%d]: %s \n", index, main_remain[index]);
        index ++;
    }

    g_free(main_str);
    g_free(main_file);
    g_strfreev(main_array);
    g_strfreev(main_remain);
#endif

#if 1 // main_entries2
    g_print("main2_uint: %d \n", main2_uint);
    g_print("main2_hide_int: %d \n", main2_hide_int);
    g_print("main2_hide_str: %s \n", main2_hide_str);
    g_free(main2_hide_str);
#endif

#if 1 // main_entries3
    g_print("cbk_opt_flag:%s \n", cbk_opt_flag?"TRUE":"FALSE");
    g_print("cbk_opt_int:%d \n", cbk_opt_int);
#endif

#if 1 //group entries
    g_print("group1_int: %d \n", group1_int);
    g_print("group1_str: %s \n", group1_str);
    g_free(group1_str);

    g_print("group2_int: %d \n", group2_int);
    g_print("group2_str: %s \n", group2_str);
    g_free(group2_str);
#endif

    g_option_context_free (context);

    return 0;
}

gint main(gint argc, gchar **argv)
{
    g_option_test(argc, argv);
    return 0;
}